/**
 *
 */
package com.feizhi.common.crypto;

import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

/**
 * RSA 相关
 * 
 * @author shuaqiu 2013年12月31日
 * 
 */
public class RSA {

    /** 生成密钥的算法 */
    private static final String RSA_ALG = "RSA";

    /** 进行加密解密的算法 */
    private static final String RSA_CIPHER_ALG = "RSA/ECB/PKCS1Padding";

    /**
     * 生成密钥
     *
     * @return
     * @throws NoSuchAlgorithmException
     */
    public static Keys generateKey() throws NoSuchAlgorithmException {
        final KeyPairGenerator generator = KeyPairGenerator.getInstance(RSA_ALG);
        generator.initialize(2048);
        final KeyPair keyPair = generator.generateKeyPair();
        return new Keys(keyPair.getPublic(), keyPair.getPrivate());
    }

    /**
     * 进行加密
     *
     * @param key
     * @param bytes
     * @return
     * @throws NoSuchAlgorithmException
     * @throws NoSuchPaddingException
     * @throws InvalidKeyException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     */
    public static byte[] encrypt(final Key key, final byte[] bytes)
            throws NoSuchAlgorithmException, NoSuchPaddingException,
            InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
        return crypt(key, bytes, Cipher.ENCRYPT_MODE);
    }

    /**
     * 进行解密
     *
     * @param key
     * @param bytes
     * @return
     * @throws NoSuchAlgorithmException
     * @throws NoSuchPaddingException
     * @throws InvalidKeyException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     */
    public static byte[] decrypt(final Key key, final byte[] bytes)
            throws NoSuchAlgorithmException, NoSuchPaddingException,
            InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
        return crypt(key, bytes, Cipher.DECRYPT_MODE);
    }

    /**
     * 进行加密解密
     *
     * @param key
     * @param bytes
     * @param mode
     * @return
     * @throws NoSuchAlgorithmException
     * @throws NoSuchPaddingException
     * @throws InvalidKeyException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     */
    private static byte[] crypt(final Key key, final byte[] bytes,
            final int mode) throws NoSuchAlgorithmException,
            NoSuchPaddingException, InvalidKeyException,
            IllegalBlockSizeException, BadPaddingException {
        final Cipher cipher = Cipher.getInstance(RSA_CIPHER_ALG);
        cipher.init(mode, key);

        final byte[] result = cipher.doFinal(bytes);
        return result;
    }

    /**
     * 根据字节码, 生成公鈅
     *
     * @param encodedKey
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static PublicKey buildPublicKey(final byte[] encodedKey)
            throws NoSuchAlgorithmException, InvalidKeySpecException {
        final KeySpec encodedKeySpec = new X509EncodedKeySpec(encodedKey);
        final KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALG);
        return keyFactory.generatePublic(encodedKeySpec);
    }

    /**
     * 根据字节码, 生成密钥
     *
     * @param encodedKey
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static PrivateKey buildPrivateKey(final byte[] encodedKey)
            throws NoSuchAlgorithmException, InvalidKeySpecException {
        final KeySpec encodedKeySpec = new PKCS8EncodedKeySpec(encodedKey);
        final KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALG);
        return keyFactory.generatePrivate(encodedKeySpec);
    }


    public static class Keys {
        private final byte[] publicKey;
        private final byte[] privateKey;

        /**
         * @param publicKey
         * @param privateKey
         */
        private Keys(final Key publicKey, final Key privateKey) {
            this.publicKey = publicKey.getEncoded();
            this.privateKey = privateKey.getEncoded();
        }

        public byte[] getPublicKey() {
            return publicKey;
        }

        public byte[] getPrivateKey() {
            return privateKey;
        }
    }
}
